/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       army_mover.cpp

	$Header: /game/army_mover.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "army_mover.h"

#include "adv_actor_action.h"
#include "adv_actor_model.h"
#include "adv_shipyard.h"
#include "adventure_ai.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_map_window.h"
#include "adventure_path_finder.h"
#include "adventure_sounds.h"
#include "angle.h"
#include "army.h"
#include "basic_dialog.h"
#include "combat_sounds.h"
#include "external_string.h"
#include "game_time.h"
#include "hero_keyword_replacer.h"
#include "music.h"
#include "options.h"
#include "platform.h"
#include "player.h"
#include "sound.h"
#include "town.h"

#include "profile.h"

// ------------------------------------------------------------------------
// compute overall movement distance
// ------------------------------------------------------------------------
static int compute_overall_distance( t_adventure_path const& path, int start )
{
	int i;
	int distance;

	distance = 0;
	for (i = start; i < path.size(); ++i)
	{
		if (path[i].direction & 1)
			distance += 48;
		else
			distance += 32;
	}
	return distance;
}


// ------------------------------------------------------------------------
// class to move army on adventure map
// Public constructor
// ------------------------------------------------------------------------
t_army_mover::t_army_mover( t_adventure_map_window* window, t_army* army, bool limit_move, 
						    t_adventure_path_finder const* path_finder )
	: t_idle_processor_no_delay(0)
{
	initialize( window, army, limit_move, path_finder, true );
}

// ------------------------------------------------------------------------
// class to move army on adventure map
// Protected constructor. Derived class constructor is responsible for
// calling initialize
// ------------------------------------------------------------------------
t_army_mover::t_army_mover( )
	: t_idle_processor_no_delay(0)
{
}


// ------------------------------------------------------------------------
// Initialize from local or derived constructor. If use_army_path is false,
// m_path must already be set up.
// ------------------------------------------------------------------------
void t_army_mover::initialize( t_adventure_map_window* window, t_army* army, 
							   bool limit_move, t_adventure_path_finder const* path_finder,
							   bool use_army_path )
{

	m_window = window;
	m_army = army;
	m_in_trigger = false;
	m_exceeded_move_limit = false;
	m_is_visible = false;
	m_movement_expended = 0;
	m_has_trigger		= false;
	m_landing			= false;
	m_limit_move		= limit_move;
	m_original_position	= army->get_position();
	
	t_adventure_map&		map = *army->get_map();

	if ( use_army_path )
	{
		t_adventure_path		path = army->get_path();

		int						i;
		int						move_left = army->get_movement();
		bool					is_boat = army->is_boat();
		bool					enemy_enforced_dest = false;
		t_creature_array const*	restricting_enemy = NULL;
		t_adv_map_point			restricted_dest;
		t_player*				owner = army->get_owner();
		bool					is_computer = owner && owner->is_computer();
		bool					built_boat = false;
		bool					hidden_enemy = false;

		// copy path until we run out of movement or encounter a trigger
		for (i = path.size() - 1; i >= 0; i--)
		{
			t_adventure_path_point& point = path[i];

			// If the path goes into the attack radius of a hidden enemy
			// then truncate the path
			if (	path_finder != 0
				&&	point.actual_enemy_adjacent
				&&	point.actual_enemy_distance > 0
				&&	(	!point.visible_enemy_adjacent
					||	point.actual_enemy_direction != point.visible_enemy_direction
					||	point.actual_enemy_distance != point.visible_enemy_distance ))
			{
				restricting_enemy = find_enemy( map, *path_finder, point, *m_army );
				if (restricting_enemy != 0)
				{
					int stealth_advantage;

					stealth_advantage = restricting_enemy->get_stealth_level()
									  - m_army->get_anti_stealth_level();
					if (stealth_advantage <= 0)
						break;
					if (stealth_advantage == 1 || restricting_enemy->get_owner_number() < 0)
						hidden_enemy = true;
				}
			}
			if ( path_finder && ( point.visible_restricts_movement || enemy_enforced_dest ) )
			{
				if (enemy_enforced_dest)
				{
					if ( (point != restricted_dest) && (point != restricting_enemy->get_position()) )
						break;
				}
				else
				{
					restricting_enemy = find_restrictive_enemy( map, *path_finder, point, *m_army );
					if (restricting_enemy != NULL)
						enemy_enforced_dest = true;
				}
				restricted_dest = point + get_direction_offset( point.actual_enemy_direction );
			} 

			if (point.move_cost > move_left && limit_move) {
				
				m_actual_destination = point.last_point;
				m_exceeded_move_limit = true;
				break;
			}
			if (map.get_trigger_object( point.last_point, point.direction, army ))
			{
				m_has_trigger   = true;
				m_trigger_point = point;
			}
			if (is_boat)
			{
				if (!map.is_ocean( point ))
				{
					m_landing = true;
					m_trigger_point = point;
					break;
				}
			}
			else
				if (is_computer && !m_has_trigger && map.is_ocean( point ) && !m_exceeded_move_limit)
				{
					t_adventure_tile const& last_tile = map[point];

					t_hero*	most_spell_points = NULL;
					if ( map.has_empty_boat( *m_army->get_owner() ) )
					{
						int		most_spell_points_count = 0;
						for (int i = 0; i < t_creature_array::k_size; i++)
						{
							t_hero* hero = (*m_army)[i].get_hero();
							if (hero && hero->can_cast( k_spell_summon_boat ))
							{
								int cur_spell_points = hero->get_spell_points();
								if ( (most_spell_points == NULL) || ( cur_spell_points > most_spell_points_count ) )
								{
									most_spell_points = hero;
									most_spell_points_count = cur_spell_points;
								}
							}
						}
					}

					if (most_spell_points != NULL)
					{
						army->get_adventure_frame()->ai_cast_summon_boat( *m_army, *most_spell_points, point );
					}
					else
						if (last_tile.ai_can_build_ship())
						{
							t_adventure_object& object = map.get_adv_object(last_tile.ai_get_shipyard_id());
							t_adv_shipyard* shipyard = dynamic_cast< t_adv_shipyard* >( &object );
							t_adv_map_point boat_position;
							if (shipyard != NULL) 
								built_boat = shipyard->find_new_ship_position( boat_position );
							else
							{
								t_town* town = object.get_town();
								if (town)
								{
									if ( !town->has( k_town_shipyard) )
									{
										if (town->has_built())
											break;
										town->buy_building( k_town_shipyard );
									}
									built_boat = town->find_new_ship_position( boat_position, false );
								}
							}

							if (built_boat)
							{
								t_army* boat = new t_army( army->get_creatures().get_leader().get_alignment() );
								boat->set_owner( army->get_owner_number() );
								boat->place( map, boat_position );
								owner->spend( army->get_ai()->get_boat_cost() ); // This destination pick wouldn't have been 
																				 // made unless he already had the cash.
							}
						}
				}
		
			if (map[point].blocks_army( *army, map, point.on_bridge, k_path_search_move_army ))
				break;
			// if the path actually enters a cell of an enemy, stop.
			if (point.actual_enemy_adjacent && point.actual_enemy_distance == 0)
				break;
			m_path.push_back( point );
			if (m_has_trigger || built_boat || hidden_enemy)
				break;
		}
	}
	
	m_halted = false;
	m_step = -1;
	m_distance = compute_overall_distance( m_path, 0 );
	if (m_path.size() == 0)
	{
		m_cell_distance = 0;
		m_crossing_point = -1;
		m_distance = 0;
		m_frames = 0;
		m_divisor = 1;
		m_delta = t_screen_point(0,0);
		m_offset = t_screen_point(0,0);
		m_distance_delta = 0;
		m_is_visible = !m_army->hidden_by_fog_of_war( map.get_player().get_team() );
	}
	else
	{
		m_army->set_action( k_adv_actor_action_prewalk );
		start_new_square();
		prepare_move( 0 );
	}
	set_next_time( get_time() );
}

t_army_mover::~t_army_mover()
{}

t_army* t_army_mover::get_army() const
{
	return m_army;
}

// ------------------------------------------------------------------------
// this function is called in the center of each new square
// ------------------------------------------------------------------------
void t_army_mover::start_new_square()
{
	if (m_distance == 0)
		return;

	t_adventure_map& map = *(m_army->get_map());

	m_step++;
	assert( m_step < m_path.size() );
	t_adventure_path_point& point = m_path[m_step];
	
	m_is_visible = !m_army->hidden_by_fog_of_war( map.get_player().get_team() );

	if (point.direction & 1)
		m_cell_distance = 48;
	else
		m_cell_distance = 32;
	m_distance -= m_cell_distance;
	m_crossing_point = m_cell_distance >> 1;
	m_army->turn_to( point.direction );
}

// ------------------------------------------------------------------------
// compute how many frames to play before we enter the next cell
// ------------------------------------------------------------------------
void t_army_mover::prepare_move( int frame )
{
	t_adventure_map const&				map = *m_army->get_map();
	t_screen_point						old_screen_point;
	t_screen_point						new_screen_point;
	int									animation_distance;
	int									overall_distance = m_distance + m_cell_distance;
	t_adv_actor_model const&			model = m_army->get_model();
	t_adv_actor_model_definition const& definition = model.get_definition();
	int									prewalk_distance = 0;
	int									walk_distance    = definition.get_walk_length();
	int									postwalk_distance = definition.get_postwalk_length();
	t_adv_actor_action_id				action = m_army->get_action();
	int									frame_count = m_army->get_frame_count();
	int									walk_cycles;
	int									distance;

	if (action == k_adv_actor_action_prewalk && frame < frame_count)
	{
		prewalk_distance = definition.get_prewalk_length();
		prewalk_distance = prewalk_distance * (frame_count - frame) / frame_count;
	}
	if (action == k_adv_actor_action_postwalk)
	{
		walk_cycles = 0;
		postwalk_distance = (frame_count - frame) * postwalk_distance / frame_count;
		animation_distance = postwalk_distance;
	}
	else
	{
		int remainder;

		distance = overall_distance - prewalk_distance - postwalk_distance;
		if (action == k_adv_actor_action_walk)
		{
			distance += frame * walk_distance / frame_count;
		}
		walk_cycles = distance / walk_distance;
		if (walk_cycles < 0)
			walk_cycles = 0;
		remainder = distance - walk_cycles * walk_distance;
		if ((prewalk_distance + postwalk_distance) * 2 < remainder)
			walk_cycles++;
		if (action == k_adv_actor_action_walk && frame != 0 && walk_cycles == 0)
			walk_cycles = 1;
		animation_distance = (walk_distance * walk_cycles) + postwalk_distance + prewalk_distance;
		if (action == k_adv_actor_action_walk && frame != 0 )
		{
			animation_distance -= frame * walk_distance / frame_count;
		}
	}

	if (action == k_adv_actor_action_prewalk && frame < frame_count)
	{
		// calculate distance for remaining prewalk frames
		distance = prewalk_distance * overall_distance / animation_distance;
		m_frames = frame_count - frame;
	}
	else if (walk_cycles > 0)
	{   // compute the distance for remaining walk frames
		distance = walk_cycles * walk_distance;
		if (action == k_adv_actor_action_walk)
		{
			distance -= frame * walk_distance / frame_count;
		}
		else
		{
			m_army->set_action( k_adv_actor_action_walk );
			frame_count = m_army->get_frame_count();
			frame = 0;
		}
		distance = distance * overall_distance / animation_distance;
		m_frames = walk_cycles * frame_count - frame;
	}
	else
	{
		if (action == k_adv_actor_action_postwalk)
		{
			m_frames = frame_count - frame;
		}
		else
		{
			m_army->set_action( k_adv_actor_action_postwalk );

			m_frames = m_army->get_frame_count();
		}
		distance = m_cell_distance;
		if (m_frames < 1)
			m_frames = 1;
	}

	assert( m_step < m_path.size() );
	t_adventure_path_point& point = m_path[m_step];

	old_screen_point = get_screen_point( map, *m_army, m_army->get_position() )
		               + m_army->get_frame_offset();
	new_screen_point = get_screen_point( map, *m_army, point );
	m_delta = new_screen_point - old_screen_point;
	m_distance_delta = m_cell_distance;

	if (distance < 0)
		distance = 0;
	// next waypoint is the center of the next cell
	// if distance is less than waypoint distance, recalculate the waypoint
	if (distance < m_cell_distance)
	{
		if ( distance > 0)
		{
			m_distance_delta = distance;
			m_delta			 = m_delta * distance / m_cell_distance;
		}
	}
	else if (distance > m_cell_distance && m_cell_distance > 0)
	{
	// if distance is >= waypoint distance, reduce the # of frames.
		m_frames = (m_frames * m_cell_distance + distance - 1) / distance;
	}

	// m_frames is now the number of frames to play until the center of
	// the next cell
	t_uint32 delay;

	assert( m_frames > 0 );

	m_divisor		  = m_frames;
	m_cell_distance  *= m_frames;
	m_crossing_point *= m_frames;
	m_offset		  = m_army->get_frame_offset() * m_frames;
	delay			  = m_army->get_frame_delay();
	if (m_army->get_owner_number() != map.get_player_number())
		set_delay( delay * 100 / get_enemy_animation_speed() );
	else
		set_delay( delay * 100 / get_army_animation_speed() );
}

// ------------------------------------------------------------------------
// class to move army on adventure map
// ------------------------------------------------------------------------
void t_army_mover::expend_movement( int total_cost )
{
	int new_cost = total_cost - m_movement_expended;

	m_movement_expended = total_cost;
	m_army->change_movement( -new_cost );
}

// ------------------------------------------------------------------------
// class to move army on adventure map
// ------------------------------------------------------------------------
void t_army_mover::activate_trigger()
{
	assert( m_has_trigger );
	if (m_army->get_map()->is_game_over())
		return;

	if ( !m_army->is_on_map() )
		return; // Destroyed in trigger_event(), usually

	m_army->get_map()->activate_trigger( m_army, m_trigger_point.last_point,
										 m_trigger_point.direction,
										 m_window->get_frame() );
}

// ------------------------------------------------------------------------
// class to move army on adventure map
// ------------------------------------------------------------------------
void t_army_mover::on_idle()
{
	t_adventure_map&				map = *m_army->get_map();

	if (map.get_current_player_number() != map.get_player_number()
		&& !show_enemy_moves())
		m_is_visible = false;
	do 
	{
		if (m_is_visible)
			do_movement();
		else
			do_hidden_move();
	} while (!m_is_visible && !m_halted);
}

// ------------------------------------------------------------------------
// move the final step in the path
// ------------------------------------------------------------------------
void t_army_mover::finish_path()
{
	declare_timer( timer_1, "finish_path" );
	end_move();
	if (m_has_trigger || m_landing)
	{	
		////////////////////////////////////////////////////////////
		// replay functionality
		
		m_army->get_map()->record_look_trigger_event( m_army, m_trigger_point.direction );
		
		////////////////////////////////////////////////////////////
		
		m_army->turn_to( m_trigger_point.direction );
		
		if (m_army->get_position() != m_trigger_point)
		{
			assert( m_army->get_position() == m_trigger_point.last_point );
			expend_movement( m_trigger_point.move_cost );
		}
		redraw_windows();
		if (m_has_trigger) 
		{
			assert( !m_landing );
			declare_timer( timer_4, "Activate trigger" );
			trigger_event();
			activate_trigger();
		}
		else
		{
			assert( m_army->is_boat() );
			assert( m_landing );
			m_army->leave_boat( m_trigger_point );
			trigger_event();
		}
	}
	else
		trigger_event();
	on_end();
}

// ------------------------------------------------------------------------
// do movement that's hidden
// ------------------------------------------------------------------------
void t_army_mover::do_hidden_move()
{
	if (m_is_visible)
		return;
	if (m_path.size() == 0)
	{
		finish_path();
		return;
	}

	t_adventure_map const&  map = *m_army->get_map();
	t_player&				player = map.get_player();
	int						team = player.get_team();

	declare_timer( timer_1, "do_hidden_movement" );
	while (!m_is_visible && !m_halted)
	{
		t_adventure_path_point& point = m_path[m_step];

		m_army->move( point );
		expend_movement( point.move_cost );
		mark_eluded_armies();
		if (m_step == m_path.size() - 1)
		{
			finish_path();
			return;
		}
		trigger_event();
		if (m_halted) 
			return;
		m_is_visible = !m_army->hidden_by_fog_of_war( team ) && show_enemy_moves();
		if (m_is_visible)
			break; // break here, because start_new_square will increment step.
		++m_step;
		declare_timer( timer_2, "on_starting_new_square: hidden" );
		on_starting_new_square();
	}
	if (m_halted)
		return;
	m_army->set_action( k_adv_actor_action_walk );
	m_distance = compute_overall_distance( m_path, m_step + 1 );
	start_new_square();
	prepare_move( 0 );
	set_next_time( get_time() + get_delay() );
}

// ------------------------------------------------------------------------
// class to move army on adventure map
// ------------------------------------------------------------------------
void t_army_mover::do_movement()
{
	int							frame = m_army->get_frame();
	int							frame_count = m_army->get_frame_count();
	t_adv_actor_action_id		action = m_army->get_action();
	t_uint32					current_time = get_time();
	t_uint32					next_time = get_next_time() - get_delay();
	t_counted_ptr<t_army_mover> ref = this;
	t_window_ptr				adv_frame = m_window->get_frame();	// temporary reference to prevent the adventure frame
																	// from removing itself if the game is over
	bool						entered_new_square = false;

	declare_timer( timer_1, "do_movement" );
	while ( m_frames > 0 && (!m_is_visible || elapsed_time( current_time, next_time ) >= 0) )
	{
		m_offset += m_delta;
		m_cell_distance -= m_distance_delta;
		if (m_cell_distance <= m_crossing_point)
		{
			enter_new_square();
		}
		m_army->set_frame_offset( m_offset / m_divisor );
		frame++;
		if (action == k_adv_actor_action_walk && frame == frame_count)
			frame = 0;
		if (frame < frame_count)
			m_army->set_frame( frame );
		m_frames--;
		if (m_is_visible)
			next_time += get_delay();
	}
	if (m_is_visible)
		set_next_time( next_time );
	// if m_frames is zero, we've hit the center of the cell, or we've
	// finished a prewalk / walk / postwalk sequence
	if (m_frames == 0)
	{
		if ((action == k_adv_actor_action_postwalk || m_path.size() == 0)
			&& m_distance + m_cell_distance == 0)
		{
			finish_path();
			return;
		}
		if (m_cell_distance == 0) 
		{
			start_new_square();

			entered_new_square = true;

			trigger_event();
			if (m_halted) 
				return;
		}
		else
		{
			m_cell_distance /= m_divisor;
			m_crossing_point /= m_divisor;
		}
		prepare_move( frame );
	}

	if (entered_new_square)
	{
		declare_timer( timer_2, "on_starting_new_square" );
		on_starting_new_square();
	}
}

// ------------------------------------------------------------------------
// class to move army on adventure map
// ------------------------------------------------------------------------
void t_army_mover::enter_new_square()
{
	assert( m_step < m_path.size() );
	assert( m_crossing_point > 0 );

	t_adventure_map const&  map = *m_army->get_map();
	t_adventure_path_point& point = m_path[m_step];
	t_screen_point			old_screen_point;
	t_screen_point			new_screen_point;
	t_screen_point			delta;

	m_crossing_point = -m_divisor;
	old_screen_point = get_screen_point( map, *m_army, m_army->get_position() );
	new_screen_point = get_screen_point( map, *m_army, point );
	delta = new_screen_point - old_screen_point;
	delta *= m_divisor;
	m_offset -= delta;
	m_army->move( point );
	expend_movement( point.move_cost );
	mark_eluded_armies();
	if (m_is_visible && !map.get_map_window()->is_visible( point ) )
	{
		map.get_map_window()->center_view( point );
	}

	// play appropriate movement sound
	t_level_map_point_2d const &	army_point = m_army->get_creatures().get_position();
	if ( !m_movement_sound && m_is_visible )
	{
		t_creature_stack &				leader	= m_army->get_creatures().get_leader();
		t_sound_ptr						sound;
		t_adv_actor_action_id			action = m_army->get_action();
	
		if (m_army->is_boat())
		{
			static t_sound_cache	boat_sound_cache( "hero ship.walk" ) ;

			sound =  boat_sound_cache.get();
		}
		else
			sound = leader.get_sound( action );

		if ( sound )
			m_movement_sound = play_adventure_sound( *m_army, sound, army_point, 
													 get_sound_volume() );
	}

	if ( m_movement_sound && m_is_visible )
	{
		// update the movement sound's position so the sound follows the owner army
		t_level_map_point_2d const & sound_point = m_army->get_creatures().get_position();

		if( m_movement_sound.get() )
			m_movement_sound->set_position( sound_point );
	}

	// update the active sounds queue
	bool is_computer = false;
	if( m_army )
	{
		t_player* owner = m_army->get_owner();
		is_computer = owner && owner->is_computer();
	}

	if( m_is_visible && !is_computer )
		update_active_sounds( army_point, map );
}

// ------------------------------------------------------------------------
// mark a particular army as eluded for experience
// ------------------------------------------------------------------------
void t_army_mover::mark_eluded_army( t_army const& enemy )
{
	int		i;
	int		j;
	t_hero* hero;
	int		experience;

	declare_timer( timer_1, "mark eluded armies" );
	for (i = 0; i < t_creature_array::k_size; ++i)
	{
		hero = (*m_army)[i].get_hero();
		if (hero == 0)
			continue;
		if (!hero->mark_eluded( enemy ))
			continue;
		experience = enemy.get_experience_value() / 4;
		hero->add_experience( experience );
		for (j = 0; j < m_stealth_experience.size(); ++j)
			if (hero == m_stealth_experience[j].hero)
				break;
		if (j == m_stealth_experience.size())
		{
			t_stealth_experience new_item;

			new_item.hero = hero;
			new_item.experience = 0;
			m_stealth_experience.push_back( new_item );
		}
		m_stealth_experience[j].experience += experience;
	}
}

// ------------------------------------------------------------------------
// gain experience from passing by enemy armies
// ------------------------------------------------------------------------
void t_army_mover::mark_eluded_armies()
{
	if (m_army->get_owner_number() < 0)
		return;
	if (m_army->get_living_hero_count() == 0)
		return;

	t_skill_mastery stealth_level = m_army->get_stealth_level();

	if (stealth_level < k_mastery_advanced)
		return;

	t_map_rect_2d			rect;
	t_adv_map_point			point = m_army->get_position();
	t_adventure_map const&	map = *m_army->get_map();

	rect.top_left.row = point.row - 2;
	rect.top_left.column = point.column - 2;
	rect.bottom_right.row = point.row + 2;
	rect.bottom_right.column = point.column + 2;

	for (point.row = rect.top(); point.row < rect.bottom(); ++point.row)
	{
		for (point.column = rect.left(); point.column < rect.right(); ++point.column)
		{
			if (!map.is_valid( point ))
				continue;

			t_adventure_tile const& tile = map[point];

			if (!tile.is_trigger( point.on_bridge ))
				continue;

			t_adventure_object const* object;
			t_army const* enemy;
			const std::vector<int>& triggers = tile.get_triggers(point.on_bridge);
			for (std::vector<int>::const_iterator	index = triggers.begin(); index != triggers.end(); ++index)
			{
				object = &map.get_adv_object( *index );
				enemy = dynamic_cast<t_army const*>( object );
				if (enemy == 0)
					continue;
				if (enemy->get_owner_number() >= 0)
					continue;
				if (enemy->get_anti_stealth_level() + 1 >= stealth_level)
					continue;
				mark_eluded_army( *enemy );
			}
		}
	}
}

// ------------------------------------------------------------------------
// report stealth gains
// ------------------------------------------------------------------------
static t_external_string const k_text_stealth_use_singular( "stealth_use_singular.misc" );
static t_external_string const k_text_stealth_use_plural( "stealth_use_plural.misc" );

void t_army_mover::report_stealth()
{
	if (m_stealth_experience.empty())
		return;

	if (m_army->is_human())
	{
		t_counted_ptr<t_basic_dialog>	dialog;
		int								i;
		std::string						text;
		t_hero_keyword_replacer			replacer;

		dialog = new t_basic_dialog;

		for (i = 0; i < m_stealth_experience.size(); ++i)
		{
			dialog->add_experience( m_stealth_experience[i].experience, 
									m_stealth_experience[i].hero );
			replacer.add_hero( m_stealth_experience[i].hero );
		}
		if (m_stealth_experience.size() == 1)
			text = replacer( k_text_stealth_use_singular );
		else
			text = replacer( k_text_stealth_use_plural );
		dialog->set_text( text );
		dialog->add_ok_button();
		dialog->open();
		dialog->run_modal();
	}
	m_army->check_hero_levels();
	m_stealth_experience.clear();
}

// ------------------------------------------------------------------------
// class to move army on adventure map
// ------------------------------------------------------------------------
void t_army_mover::end_move()
{
	if (m_halted)
		return;
	declare_timer( timer_1, "end_move" );

	////////////////////////////////////////////////////////////
	// functionality

	m_army->get_map()->record_mover_event( m_army, m_path, m_limit_move, m_original_position);

	////////////////////////////////////////////////////////////

	m_halted = true;
	suspend_idle_processing();
	m_window->get_frame()->reset_pathfinder();
	m_army->set_action( k_adv_actor_action_wait );
	m_army->set_frame_offset( t_screen_point(0,0) );
	redraw_windows();
	m_window->clear_enemy_attack();
	m_window->halt_movement();
	if (m_movement_sound != 0)
	{
		m_movement_sound->get_sound_ptr()->stop();
		m_movement_sound = 0;
	}
	report_stealth();
}

// ------------------------------------------------------------------------
// class to move army on adventure map
// ------------------------------------------------------------------------
void t_army_mover::cancel_move()
{
	if (m_halted)
		return;

	t_counted_ptr<t_army_mover> ref = this;
	t_window_ptr				frame = m_window->get_frame();	// temporary reference to prevent the adventure frame
																// from removing itself if the game is over
	end_move();

	trigger_event();
	
	if (m_has_trigger && m_army->get_position() == m_trigger_point)
		activate_trigger();
	
	on_end();
}

// ------------------------------------------------------------------------
// class to move army on adventure map
// ------------------------------------------------------------------------
void t_army_mover::on_end()
{
	if (m_army->get_map()->is_game_over())
		return;

	t_player* owner = m_army->get_owner();
	t_adventure_map* map = m_army->get_map();

	if ((owner != NULL) 
			&& (&map->get_current_player() == owner) 
			&& !owner->is_computer() 
			&& owner->is_eliminated())
	{
		m_army->get_adventure_frame()->force_end_turn();
		return;
	}

	if (m_in_trigger == false)
	{
		m_window->check_enemy_attack( m_army );
	}
}

// ------------------------------------------------------------------------
// class to move army on adventure map
// ------------------------------------------------------------------------
void t_army_mover::trigger_event()
{
	t_counted_ptr< t_army_mover > ref = this;

	bool was_halted = m_halted;

	if (m_in_trigger) 
		return;
	
	t_adventure_map* adventure_map = m_army->get_map();

	if (adventure_map->is_game_over())
		return;

	m_in_trigger = true;

	m_army->trigger_events();

	m_in_trigger = false;

	if (!m_halted)
		adventure_map->clear_selection();
	if (m_halted && !was_halted)
		on_end();
}

bool t_army_mover::is_done() const
{
	return m_halted;
}

bool t_army_mover::is_multiple_turn_move() const
{
	return m_exceeded_move_limit;
}

t_adv_map_point const& t_army_mover::get_actual_destination() const
{
	return m_actual_destination;
}

void t_army_mover::on_starting_new_square()
{
}

// ------------------------------------------------------------------------
// class to move enemy army on adventure map
// ------------------------------------------------------------------------
t_enemy_army_mover::t_enemy_army_mover( t_adventure_map_window* window, t_army* army )
                  : t_army_mover( window, army, false, 0 )
{
}

// ------------------------------------------------------------------------
// class to move enemy army on adventure map
// ------------------------------------------------------------------------	
t_enemy_army_mover::~t_enemy_army_mover()
{
}

// ------------------------------------------------------------------------
// class to move enemy army on adventure map
// ------------------------------------------------------------------------
void t_enemy_army_mover::expend_movement( int arg )
{
}

// ------------------------------------------------------------------------
// class to move enemy army on adventure map
// ------------------------------------------------------------------------
void t_enemy_army_mover::on_end()
{
	if (m_army->get_map()->is_game_over())
		return;

	int			difference;
	int			best_difference;
	t_direction direction;

	direction = m_army->get_direction();
	best_difference = (direction - k_direction_southwest + 8) & 7;
	if (best_difference > 4)
		best_difference = 4 - best_difference;
	difference = (direction - k_direction_southeast + 8) & 7;
	if (difference > 4)
		difference = 4 - difference;
	if (difference < best_difference)
		direction = k_direction_southeast;
	else
		direction = k_direction_southwest;

	m_army->set_direction( direction );
}

// ------------------------------------------------------------------------
// class to move enemy army on adventure map
// ------------------------------------------------------------------------
t_enemy_army_attack::t_enemy_army_attack( t_adventure_map_window* window, t_army* army, 
									    t_army* target )
                  : t_enemy_army_mover( window, army )
{
	m_target = target;
}

t_enemy_army_attack::~t_enemy_army_attack()
{
}

// ------------------------------------------------------------------------
// class to move enemy army on adventure map
// ------------------------------------------------------------------------
void t_enemy_army_attack::activate_trigger()
{
	if (m_in_trigger)
		return;

	m_in_trigger = true;
	
	t_direction direction;
	int			angle;

	angle = get_angle( m_target->get_position(), m_army->get_position() );
	direction = get_direction( angle );
	m_army->touch_armies( m_target, m_army->get_position(), direction,
		                  m_window->get_frame(), m_original_position );

	m_in_trigger = false;
}

// ------------------------------------------------------------------------
// class to move enemy army on adventure map
// ------------------------------------------------------------------------
void t_enemy_army_attack::on_end()
{	
	if (m_army->get_map()->is_game_over())
		return;

	if (!m_army->empty() && !m_army->is_graveyard())
	{
		if (m_army->get_position() != m_original_position)
			m_window->move_enemy_army( m_army, m_original_position );
	}
	else
		m_window->clear_enemy_attack();
}


